/*多路存储服务器
 日期：20150125
 */
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"

// 环境参数
unsigned fileSinkBufferSize0 = 200000;
char eventLoopWatchVariable = 0;
Boolean outPutAviFile = true;
static unsigned rtspClientCount = 0; // 记录有多少RTSPClient正在使用
#define RTSP_CLIENT_VERBOSITY_LEVEL 1 // 默认输出调试信息
#define REQUEST_STREAMING_OVER_TCP False //是否使用TCP连接

// RTSP响应处理事件
void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode,
		char* resultString);
void continueAfterSETUP(RTSPClient* rtspClient, int resultCode,
		char* resultString);
void continueAfterPLAY(RTSPClient* rtspClient, int resultCode,
		char* resultString);

// 其他事件句柄
void subsessionAfterPlaying(void* clientData); // 当子媒体流停止时调用
void subsessionByeHandler(void* clientData); // 当子媒体流接收到RTCP（BYE）时调用
void streamTimerHandler(void* clientData); // 处理流媒体结束时的持续时间（未接收到BYE命令）
void periodicFileOutputTimerHandler0(RTSPClient * rtspClient); //写文件事件
void sessionAfterPlaying0(void * rtspClient);

void openURL(UsageEnvironment& env, char const* progName, char const* rtspURL);

//用迭代器为每个子媒体会话建立连接
void setupNextSubsession(RTSPClient* rtspClient);

//关闭一个流媒体连接（包括RTSPClient对象）
void shutdownStream(RTSPClient* rtspClient, int exitCode = 1);

UsageEnvironment& operator<<(UsageEnvironment& env,const RTSPClient& rtspClient) {
	return env << "[URL:\"" << rtspClient.url() << "\"]: ";
}

UsageEnvironment& operator<<(UsageEnvironment& env,const MediaSubsession& subsession) {
	return env << subsession.mediumName() << "/" << subsession.codecName();
}

void usage(UsageEnvironment& env, char const* progName) {
	env << "Usage: " << progName << " <rtsp-url-1> ... <rtsp-url-N>\n";
	env << "\t(where each <rtsp-url-i> is a \"rtsp://\" URL)\n";
}

int main(int argc, char** argv) {
	TaskScheduler* scheduler = BasicTaskScheduler::createNew();
	UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);

	if (argc < 2) {
		usage(*env, argv[0]);
		return 1;
	}
	for (int i = 1; i <= argc - 1; ++i) {
		openURL(*env, argv[0], argv[i]);
	}
	env->taskScheduler().doEventLoop(&eventLoopWatchVariable);
	return 0;
}

//定义一个StreamClientState类来维护每个RTSPClient的生命周期
class StreamClientState {
public:
	StreamClientState();
	virtual ~StreamClientState();

public:
	MediaSubsessionIterator* iter;
	MediaSession* session;
	MediaSubsession* subsession;
	TaskToken streamTimerTask;
	double duration;
};

//继承RTSPClient，定制自己的RTSPClient，实现多种方法
class ourRTSPClient: public RTSPClient {
public:
	static ourRTSPClient* createNew(UsageEnvironment& env, char const* rtspURL,
			int verbosityLevel = 0, char const* applicationName = NULL,
			portNumBits tunnelOverHTTPPortNum = 0);
	void createPeriodicOutputFiles0();
	void createOutputFiles0(char const * periodicFilenameSuffix);
	void closeMediaSinks0();

protected:
	ourRTSPClient(UsageEnvironment& env, char const* rtspURL,
			int verbosityLevel, char const* applicationName,
			portNumBits tunnelOverHTTPPortNum);
	virtual ~ourRTSPClient();

public:
	StreamClientState scs;
	TaskToken periodicFileOutputTask0;
	unsigned fileOutputInterval; // 秒
	unsigned fileOutputSecondsSoFar; // 秒
	Medium* Out0;
	double initialSeekTime0;
	float scale0;
	double endTime0;
};

void openURL(UsageEnvironment& env, char const * progName,char const* rtspURL) {
	RTSPClient* rtspClient = ourRTSPClient::createNew(env, rtspURL,RTSP_CLIENT_VERBOSITY_LEVEL, progName);
	if (rtspClient == NULL) {
		env << "Failed to create a RTSP client for URL \"" << rtspURL << "\": "
				<< env.getResultMsg() << "\n";
		return;
	}
	++rtspClientCount;
	//发送DESCRIBE命令，来获取SDP信息，这个命令并不会阻塞程序，响应事件在eventloop中处理
	rtspClient->sendDescribeCommand(continueAfterDESCRIBE);
}

//RTSP响应事件的实现
void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode,char* resultString) {
	do {
		UsageEnvironment& env = rtspClient->envir(); // alias
		StreamClientState& scs = ((ourRTSPClient*) rtspClient)->scs; // alias

		if (resultCode != 0) {
			env << *rtspClient << "Failed to get a SDP description: "
					<< resultString << "\n";
			delete[] resultString;
			break;
		}
		char* const sdpDescription = resultString;
		env << *rtspClient << "Got a SDP description:\n" << sdpDescription<< "\n";
		//根据SDP信息创建一个会话对象
		scs.session = MediaSession::createNew(env, sdpDescription);
		delete[] sdpDescription;
		if (scs.session == NULL) {
			env << *rtspClient<< "Failed to create a MediaSession object from the SDP description: "<< env.getResultMsg() << "\n";
			break;
		} else if (!scs.session->hasSubsessions()) {
			env << *rtspClient<< "This session has no media subsessions (i.e., no \"m=\" lines)\n";
			break;
		}
		//为会话创建数据源并建立连接，在每个子会话上迭代操作
		//调用MediaSubsession::initiate()方法，然后为每个子会话发送SETUP命令，每个子会话拥有自己的数据源
		scs.iter = new MediaSubsessionIterator(*scs.session);
		setupNextSubsession(rtspClient);
		return;
	} while (0);

	//发生不可逆转的错误
	shutdownStream(rtspClient);
}

void setupNextSubsession(RTSPClient* rtspClient) {
	UsageEnvironment& env = rtspClient->envir(); // alias
	StreamClientState& scs = ((ourRTSPClient*) rtspClient)->scs; // alias

	scs.subsession = scs.iter->next();
	if (scs.subsession != NULL) {
		if (!scs.subsession->initiate()) {
			env << *rtspClient << "Failed to initiate the \"" << *scs.subsession<< "\" subsession: " << env.getResultMsg() << "\n";
			setupNextSubsession(rtspClient); //放弃这个子会话，进入下一个子会话
		} else {
			env << *rtspClient << "Initiated the \"" << *scs.subsession<< "\" subsession (";
			if (scs.subsession->rtcpIsMuxed()) {
				env << "client port " << scs.subsession->clientPortNum();
			} else {
				env << "client ports " << scs.subsession->clientPortNum() << "-"<< scs.subsession->clientPortNum() + 1;
			}
			env << ")\n";
			//继续为这个子会话建立连接，发送SETUP命令
			rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP,False, REQUEST_STREAMING_OVER_TCP);
		}
		return;
	}

	((ourRTSPClient *) rtspClient)->createPeriodicOutputFiles0();  //文件输出

	//为所有子会话建立连接成功，发送PLAY命令，开始传送流媒体
	if (scs.session->absStartTime() != NULL) {
		//流媒体使用绝对时间
		rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY,scs.session->absStartTime(), scs.session->absEndTime());
	} else {
		//流媒体使用相对时间
		scs.duration = scs.session->playEndTime() - scs.session->playStartTime();
		rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY);
	}
}

void continueAfterSETUP(RTSPClient* rtspClient, int resultCode,
		char* resultString) {
	do {
		UsageEnvironment& env = rtspClient->envir(); // alias
		StreamClientState& scs = ((ourRTSPClient*) rtspClient)->scs; // alias

		if (resultCode != 0) {
			env << *rtspClient << "Failed to set up the \"" << *scs.subsession<< "\" subsession: " << resultString << "\n";
			break;
		}
		env << *rtspClient << "Set up the \"" << *scs.subsession<< "\" subsession (";
		if (scs.subsession->rtcpIsMuxed()) {
			env << "client port " << scs.subsession->clientPortNum();
		} else {
			env << "client ports " << scs.subsession->clientPortNum() << "-"<< scs.subsession->clientPortNum() + 1;
		}
		env << ")\n";

		//为子会话建立连接成功，可以创建一个sink，然后调用startPlaying（）函数【这将创建一个sink准备接受数据，实际接受数据是在发送PLAY命令之后】

		/*暂时不用创建子回话sink*/
		//scs.subsession->sink = DummySink::createNew(env, *scs.subsession, rtspClient->url());
		printf("Height : %d \t Width: %d \tFPS: %d\n",scs.subsession->videoHeight(),scs.subsession->videoWidth(),scs.subsession->videoFPS());
		if (scs.subsession->sink == NULL) {
			env << *rtspClient << "Failed to create a data sink for the \""
					<< *scs.subsession << "\" subsession: "
					<< env.getResultMsg() << "\n";
			break;
		}

		env << *rtspClient << "Created a data sink for the \"" << *scs.subsession << "\" subsession\n";
		scs.subsession->miscPtr = rtspClient; //为了让（subsession）子会话能够访问RTSPClient
		scs.subsession->sink->startPlaying(*(scs.subsession->readSource()),subsessionAfterPlaying, scs.subsession);
		//创建一个RTCP的BYE命令时间处理的句柄
		if (scs.subsession->rtcpInstance() != NULL) {
			scs.subsession->rtcpInstance()->setByeHandler(subsessionByeHandler,scs.subsession);
		}
	} while (0);
	delete[] resultString;
	//为下一个子会话建立连接（if any）
	setupNextSubsession(rtspClient);
}

void continueAfterPLAY(RTSPClient* rtspClient, int resultCode,char* resultString) {
	Boolean success = False;

	do {
		UsageEnvironment& env = rtspClient->envir(); // alias
		StreamClientState& scs = ((ourRTSPClient*) rtspClient)->scs; // alias

		if (resultCode != 0) {
			env << *rtspClient << "Failed to start playing session: "<< resultString << "\n";
			break;
		}

		//设置一个计时器，处理在流媒体结束时的持续时间
		// Set a timer to be handled at the end of the stream's expected duration (if the stream does not already signal its end
		// using a RTCP "BYE").  This is optional.  If, instead, you want to keep the stream active - e.g., so you can later
		// 'seek' back within it and do another RTSP "PLAY" - then you can omit this code.
		// (Alternatively, if you don't want to receive the entire stream, you could set this timer for some shorter value.)
		if (scs.duration > 0) {
			unsigned const delaySlop = 2; // number of seconds extra to delay, after the stream's expected duration.  (This is optional.)
			scs.duration += delaySlop;
			unsigned uSecsToDelay = (unsigned) (scs.duration * 1000000);
			scs.streamTimerTask = env.taskScheduler().scheduleDelayedTask(uSecsToDelay, (TaskFunc*) streamTimerHandler, rtspClient);
		}

		env << *rtspClient << "Started playing session";
		if (scs.duration > 0) {
			env << " (for up to " << scs.duration << " seconds)";
		}
		env << "...\n";

		success = True;
	} while (0);
	delete[] resultString;

	if (!success) {
		//发生不可逆转的错误
		shutdownStream(rtspClient);
	}
}

void subsessionAfterPlaying(void* clientData) {
	MediaSubsession* subsession = (MediaSubsession*) clientData;
	RTSPClient* rtspClient = (RTSPClient*) (subsession->miscPtr);

	//开始关闭子会话
	Medium::close(subsession->sink);
	subsession->sink = NULL;

	//检查是否所有子会话已关闭
	MediaSession& session = subsession->parentSession();
	MediaSubsessionIterator iter(session);
	while ((subsession = iter.next()) != NULL) {
		if (subsession->sink != NULL)
			return; // 这个子会话还未关闭
	}

	//所有子会话已关闭，然后停止RTSPClient
	shutdownStream(rtspClient);
}

void subsessionByeHandler(void* clientData) {
	MediaSubsession* subsession = (MediaSubsession*) clientData;
	RTSPClient* rtspClient = (RTSPClient*) subsession->miscPtr;
	UsageEnvironment& env = rtspClient->envir(); // alias

	env << *rtspClient << "Received RTCP \"BYE\" on \"" << *subsession<< "\" subsession\n";

	//当做子会话已关闭来处理
	subsessionAfterPlaying(subsession);
}

void streamTimerHandler(void* clientData) {
	ourRTSPClient* rtspClient = (ourRTSPClient*) clientData;
	StreamClientState& scs = rtspClient->scs; // alias
	scs.streamTimerTask = NULL;
	//关闭流媒体传输
	shutdownStream(rtspClient);
}

void shutdownStream(RTSPClient* rtspClient, int exitCode) {
	UsageEnvironment& env = rtspClient->envir(); // alias
	StreamClientState& scs = ((ourRTSPClient*) rtspClient)->scs; // alias

	//首先检查是否还存在子会话需要关闭
	if (scs.session != NULL) {
		Boolean someSubsessionsWereActive = False;
		MediaSubsessionIterator iter(*scs.session);
		MediaSubsession* subsession;

		while ((subsession = iter.next()) != NULL) {
			if (subsession->sink != NULL) {
				Medium::close(subsession->sink);
				subsession->sink = NULL;
				if (subsession->rtcpInstance() != NULL) {
					subsession->rtcpInstance()->setByeHandler(NULL, NULL); // 防止在处理TEARDOMN命令时服务器发送BYE命令
				}
				someSubsessionsWereActive = True;
			}
		}
		if (someSubsessionsWereActive) {
			//发送TEARDOWN命令，告诉服务器关闭流媒体传输，这个不会影响对TEARDOWN命令的处理事件
			rtspClient->sendTeardownCommand(*scs.session, NULL);
		}
	}

	env << *rtspClient << "Closing the stream.\n";
	Medium::close(rtspClient);
	//这个地方也会释放StreamClientState

	if (--rtspClientCount == 0) {
		//当没有连接时退出程序，或者用eventLoopWatchVariable = 1来替换，控制eventloop是否继续。
		exit(exitCode);
	}
}

ourRTSPClient* ourRTSPClient::createNew(UsageEnvironment& env,
		char const* rtspURL, int verbosityLevel, char const* applicationName,
		portNumBits tunnelOverHTTPPortNum) {
	return new ourRTSPClient(env, rtspURL, verbosityLevel, applicationName,
			tunnelOverHTTPPortNum);
}

void ourRTSPClient::createPeriodicOutputFiles0() {
	char periodicFileNameSuffix[100];
	snprintf(periodicFileNameSuffix, sizeof periodicFileNameSuffix,
			"-%05d-%05d", fileOutputSecondsSoFar,
			fileOutputSecondsSoFar + fileOutputInterval);
	this->createOutputFiles0(periodicFileNameSuffix);

	periodicFileOutputTask0 = envir().taskScheduler().scheduleDelayedTask(
			fileOutputInterval * 1000000,
			(TaskFunc*) periodicFileOutputTimerHandler0, this);
}

void ourRTSPClient::createOutputFiles0(char const * periodicFileNameSuffix) {
	char outFileName[1000];
	char const* prefix = this->name();
	snprintf(outFileName, sizeof outFileName, "%s%s.%s", prefix,periodicFileNameSuffix, outPutAviFile?"avi":"mp4");

	if(!outPutAviFile){
		Out0 = QuickTimeFileSink::createNew(envir(), *scs.session, outFileName,fileSinkBufferSize0, 1280, 720, 25, false, false, false, true);
		if (Out0 == NULL) {
			envir()<< "Failed to create a \"QuickTimeFileSink\" for outputting to \""
				<< outFileName << "\": " << envir().getResultMsg() << "\n";
			closeMediaSinks0();
		} else {
			envir() << "Outputting to the file: \"" << outFileName << "\"\n";
		}
		((QuickTimeFileSink *)Out0)->startPlaying(sessionAfterPlaying0, this);
	}else{
		Out0 = AVIFileSink::createNew(envir(), *scs.session, outFileName,fileSinkBufferSize0, 1280, 720, 25, false);
		if (Out0 == NULL) {
			envir()<< "Failed to create a \"AVIFileSink\" for outputting to \""
				<< outFileName << "\": " << envir().getResultMsg() << "\n";
			closeMediaSinks0();
		} else {
			envir() << "Outputting to the file: \"" << outFileName << "\"\n";
		}
		((AVIFileSink *)Out0)->startPlaying(sessionAfterPlaying0, this);
	}
}

void periodicFileOutputTimerHandler0(RTSPClient *rtspClient) {
	((ourRTSPClient*) rtspClient)->fileOutputSecondsSoFar +=
			((ourRTSPClient*) rtspClient)->fileOutputInterval;
	((ourRTSPClient*) rtspClient)->closeMediaSinks0();
	((ourRTSPClient*) rtspClient)->createPeriodicOutputFiles0();
}

void sessionAfterPlaying0(void *rtspClient) {
	if (&(((ourRTSPClient *) rtspClient)->envir()) != NULL) {
		((ourRTSPClient *) rtspClient)->envir().taskScheduler().unscheduleDelayedTask(
				((ourRTSPClient*) rtspClient)->periodicFileOutputTask0);
		((ourRTSPClient *) rtspClient)->envir().taskScheduler().unscheduleDelayedTask(
				((ourRTSPClient*) rtspClient)->scs.streamTimerTask);
	}
	((ourRTSPClient *) rtspClient)->sendPlayCommand(
			*(((ourRTSPClient*) rtspClient)->scs.session), continueAfterPLAY,
			((ourRTSPClient*) rtspClient)->initialSeekTime0,
			((ourRTSPClient*) rtspClient)->endTime0,
			((ourRTSPClient*) rtspClient)->scale0, NULL);
}

void ourRTSPClient::closeMediaSinks0() {
	Medium::close(this->Out0);
	Out0 = NULL;
	if (scs.session == NULL)
		return;
	MediaSubsessionIterator iter(*(scs.session));
	MediaSubsession* subsession;
	while ((subsession = iter.next()) != NULL) {
		Medium::close(subsession->sink);
		subsession->sink = NULL;
	}
}

ourRTSPClient::ourRTSPClient(UsageEnvironment& env, char const* rtspURL,
		int verbosityLevel, char const* applicationName,
		portNumBits tunnelOverHTTPPortNum) :
		RTSPClient(env, rtspURL, verbosityLevel, applicationName,
				tunnelOverHTTPPortNum, -1) {
	fileOutputInterval = 5; // 秒
	fileOutputSecondsSoFar = 0; // 秒
	Out0 = NULL;
	initialSeekTime0 = 0.0f;
	scale0 = 1.0f;
	endTime0 = 0.0f;
	periodicFileOutputTask0 = NULL;
}

ourRTSPClient::~ourRTSPClient() {
}

StreamClientState::StreamClientState() :
		iter(NULL), session(NULL), subsession(NULL), streamTimerTask(NULL), duration(
				0.0) {
}

StreamClientState::~StreamClientState() {
	delete iter;
	if (session != NULL) {
		//删除session并清空streamTimerTask
		UsageEnvironment& env = session->envir(); // alias
		env.taskScheduler().unscheduleDelayedTask(streamTimerTask);
		Medium::close(session);
	}
}
